using FlyIL.Expression;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using static FlyIL.AssemblerRegisters;
using static FlyIL.OperandMaker;

namespace FlyIL.Trace.Tests
{


    [TestClass()]
    public class 追踪_功能测试
    {


        //[TestMethod()]

        //public void xchg测试()
        //{
        //    var vcontext = new Context(false, 0);

        //    {

        //        vcontext.Make().mov(R2D, 0x6666);   // (6666 <- 34) +3 
        //        vcontext.Make().mov(R3D, 0x1234);   // (R2d <- 34) +3 

        //        // 此处是 xchg 的操作数 , 大于追踪的
        //        vcontext.Make().xchg(R3L, R2L);     // (R2d <- r3l) +3 
        //        vcontext.Make().add(R2D, 3);        // R2D +3 
        //        vcontext.Make().mov(R4D, R2D);      // R2D



        //        var exp = vcontext.TraceWeak(vcontext.Last, ref vcontext.Last.O1);


        //        Assert.IsTrue(exp.Calculable);

        //    }

        //    {

        //        vcontext.Make().mov(R3D, 0x1234);   // 34 + 3 

        //        // 此处是 xchg 的操作数 , 大于追踪的
        //        vcontext.Make().xchg(R3D, R2D);     // r3l +3 
        //        vcontext.Make().add(R2L, 3);        // r2l +3 
        //        vcontext.Make().mov(R4H, R2L);      // r2l



        //        var exp = vcontext.TraceWeak(vcontext.Last, ref vcontext.Last.O1);


        //        Assert.IsTrue(exp.Calculable);
        //        Assert.AreEqual(exp.Result, (ulong) 0x37);

        //    }


        //}

        [TestMethod()]

        public void 表达式需要截止()
        {
            var vcontext = new Context(false, 0);



            vcontext.Make().nop();
            vcontext.Make().push(R1D);
            vcontext.Make().push(R2D);
            vcontext.Make().cmp(R1L, _byte_[Esp + 4]);

            vcontext.Make().je(7);


            var exp = vcontext.TraceExp(vcontext.Last, vcontext.Last.ExpCon);


            Assert.IsTrue(exp.Calculable);


        }

        [TestMethod()]
        public void 栈追踪的正确性()
        {
            RegCodeLocalism.Current = PlatLocalism.Arm;


            /// 未完成的栈追踪 1 
            {

                var vcontext = new Context(false, 0);
                vcontext.Make().pop(R1D);        // esp 要增加 

                /// 前面是 pop 语句， 要追踪的栈变量， 从这一条语句看， 应该是 [esp -4] 
                /// 栈变量追踪到 负值  是没有意义的  
                vcontext.Make().mov(R2D, R1D);

                /// 此处 应该返回 [esp -4 ] 
                var exp = vcontext.TraceWeak(vcontext.Last, ref vcontext.Last.O1);


                Assert.AreEqual(exp.FinalLeft<Operand>(), _dword_[Esp + (-4)]);
            }


            /// 未完成的栈追踪 1 
            {

                var vcontext = new Context(false, 0);
                vcontext.Make().pop(R1D);        // esp 要增加 

                /// 前面是 pop 语句， 要追踪的栈变量， 从这一条语句看， 应该是 [esp -4] 
                /// 栈变量追踪到 负值  是没有意义的  
                vcontext.Make().mov(R2D, R1D);

                /// 此处 应该返回 [esp -4 ]  ,  校验之后 返回 null 
                var exp = vcontext.TraceStrict(vcontext.Last, ref vcontext.Last.O1);


                Assert.IsNull(exp);
            }


            /// 未完成的栈追踪 2 
            {

                var vcontext = new Context(false, 0);
                vcontext.Make().pop(R3D);
                vcontext.Make().pop(R3D);
                vcontext.Make().pop(R3D);
                vcontext.Make().pop(R3D);
                vcontext.Make().pop(R1D);        // esp 要增加 

                /// 前面是 pop 语句， 要追踪的栈变量， 从这一条语句看， 应该是 [esp -4] 
                /// 栈变量追踪到 负值  是没有意义的  
                vcontext.Make().mov(R2D, R1D);

                /// !!! 此处 依然应该返回 [esp -4 ] 
                var exp = vcontext.TraceWeak(vcontext.Last, ref vcontext.Last.O1);


                Assert.AreEqual(exp.FinalLeft<Operand>(), _dword_[Esp + (-4)]);
            }



            {

                var vcontext = new Context(false, 0);

                vcontext.Make().push(0x12345678);
                vcontext.Make().push(R3D);
                vcontext.Make().pop(R1D);        // esp 要增加 

                vcontext.Make().mov(R2D, R1D);


                var op = _dword_[Esp];
                var exp = vcontext.TraceWeak(vcontext.Last, ref op);

                Assert.IsNotNull(exp);
                Assert.IsTrue(exp.Calculable);

            }


            {

                var vcontext = new Context(false, 0);


                vcontext.Make().push(R3D);
                vcontext.Make().pop(R1D);        // esp 要增加 

                /// 前面是 pop 语句， 要追踪的栈变量， 从这一条语句看， 应该是 [esp -4] 
                /// 栈变量追踪到 负值  是没有意义的  
                vcontext.Make().mov(R2D, R1D);

                /// 此处 应该返回 r3d 
                var exp = vcontext.TraceWeak(vcontext.Last, ref vcontext.Last.O1);


                Assert.IsNotNull(exp);
                Assert.AreEqual(exp.FinalLeft<Operand>(), _regs_[R3D]);
            }






        }



        [TestMethod()]
        public void 需要回退的情况()
        {
            RegCodeLocalism.Current = PlatLocalism.Arm;

            var vcontext = new Context(false, 0);

            vcontext.Make().pop(R3D);
            vcontext.Make().pop(R1D);        // esp 要增加 


            /// 前面是 pop 语句， 要追踪的栈变量， 从这一条语句看， 应该是 [esp -4] 
            /// 栈变量追踪到 负值  是没有意义的  
            vcontext.Make().mov(R2D, R1D);

            var exp = vcontext.TraceStrict(vcontext.Last, ref vcontext.Last.O1);



        }


        [TestMethod()]
        public void 单目运算的追踪()
        {
            RegCodeLocalism.Current = PlatLocalism.Arm;

            var vcontext = new Context(true, 0);

            vcontext.Make().mov(R1D, 222);


        }


        [TestMethod()]
        public void 追踪一个复杂操作数1()
        {
            RegCodeLocalism.Current = PlatLocalism.Arm;

            var vcontext = new Context(true, 0);

            vcontext.Make().xor(R1D, R1D);      // load( 22 + r7d )

            vcontext.Make().mov(R1W, 22);       // load(  r1d <= 22 + r7d)
            vcontext.Make().add(R1D, R7D);      //  load ( r1d + r7d) 
            vcontext.Make().mov(R3D, _dword_[R1D]);


            var exp = vcontext.TraceStrict(vcontext.Last, ref vcontext.Last.O1);



            Assert.AreEqual(exp.FinalLeft<Operand>(), Operand.Make(4, R7D, 22));


        }




        [TestMethod()]
        public void 追踪一个复杂操作数()
        {
            RegCodeLocalism.Current = PlatLocalism.Arm;

            var vcontext = new Context(true, 0);

            vcontext.Make().mov(R1D, 222);

            vcontext.Make().add(R1D, R7D);

            vcontext.Make().mov(R3D, _dword_[R1D]);


            var exp = vcontext.TraceStrict(vcontext.Last, ref vcontext.Last.O1);



            Assert.AreEqual(exp.FinalLeft<Operand>(), Operand.Make(4, R7D, 222));

        }

        /// NOTE: !!! 注意 此处 追踪到 esp , 但是检测到 esp后面 已被更改 ， 所以需要由 语句3 继续向上查找 [esp +4] . 故找到 R1D 
        [TestMethod()]
        public void 变相的栈变量()
        {

            Context vcontext;
            vcontext = new Context(false);

            vcontext.Make().push(R1D);
            vcontext.Make().push(R2D);
            vcontext.Make().mov(R3D, Esp);
            vcontext.Make().push(R2D);
            vcontext.Make().push(R2D);
            vcontext.Make().mov(R2D, _dword_[R3D + 4]);

            var op = vcontext.Codes.Last.O2;
            var node = vcontext.TraceStrict(vcontext.Codes.Last, ref op);


            Assert.AreEqual(node.FinalLeft<Operand>(), OperandMaker._regs_[R1D]);
        }



        [TestMethod()]
        public void TraceExp2Test()
        {
            var vcontext = new Context(true);

            vcontext.Make(1).mov(R1, 0x12345);
            vcontext.Make().mov(R2, 0x12345);
            vcontext.Make().mov(R3, R2);
            vcontext.Make().add(R3, R1);
            vcontext.Make().mov(R4, R3);

            var node = vcontext.TraceStrict(vcontext.Codes.Last, ref vcontext.Last.O1);

            Assert.IsNotNull(node);

            node.Simplify();


            Assert.AreEqual(node.Depth, 1);

        }



        [TestMethod()]
        public void 栈传递()
        {


            {

                Context vcontext;
                vcontext = new Context(false);

                vcontext.Make().pop(R7D);

                vcontext.Make().mov(R1L, _byte_[R7D]);

                var op = vcontext.Codes.Last.O2;
                var node = vcontext.TraceStrict(vcontext.Codes.Last, ref op);

                /// 此处不能返回 Esp , 因为中间 有一条语句， 改变了 Esp 

                Assert.AreEqual(node.FinalLeft<Operand>(), _byte_[R7D]);

            }



            {

                Context vcontext;
                vcontext = new Context(false);

                vcontext.Make().mov(R3D, Esp);
                vcontext.Make().lea(Esp, ExprRegister.Esp - 8);
                vcontext.Make().mov(R0D, R3D);

                var op = vcontext.Codes.Last.O2;
                var node = vcontext.TraceStrict(vcontext.Codes.Last, ref op);

                /// 此处不能返回 Esp , 因为中间 有一条语句， 改变了 Esp 
            }



        }









        [TestMethod()]
        public void xorTest()
        {



            Context vcontext = new Context(true);
            vcontext.Make(1).sub(R2, R2);
            vcontext.Make().sub(R1, R2);
            vcontext.Make().mov(R3, R1);

            var node = vcontext.TraceStrict(vcontext.Codes.Last, ref vcontext.Codes.Last.O1);
            node.Simplify();

            Assert.AreEqual(node.FinalLeft<Operand>(), OperandMaker._regs_[R1]);

        }


        [TestMethod()]
        public void LeaTest()
        {

            Context vcontext = new Context(true);
            vcontext.Make(1).lea(R1, (ExprRegister.R1 + (ulong)1));
            vcontext.Make().lea(R1, (ExprRegister.R1 + (ulong)1));
            vcontext.Make().mov(R3, R1);

            var node = vcontext.TraceStrict(vcontext.Codes.Last.Previous, ref vcontext.Codes.Last.O2);
            node.Simplify();

            Assert.AreEqual(node.Depth, 1);

        }



        [TestMethod()]
        public void TraceExpTest1()
        {
            Context vcontext;
            Inst node;

            Expr exp;



            vcontext = new Context(true);

            vcontext.Make().push(1111);
            vcontext.Make().pop(R1D);

            node = vcontext.Codes.Last;
            exp = vcontext.TraceStrict(node, ref node.O1);

            Assert.IsTrue(exp.Calculable);

            vcontext = new Context(true);

            vcontext.Make().mov(R10, 0x56033BBF76A41B1E);
            vcontext.Make().push(R10);
            vcontext.Make().push(R15);
            vcontext.Make().mov(R4, _qword_[Rsp + 8]);

            vcontext.Make().call(0x777545, 1);
            vcontext.Make().pop(R6);
            vcontext.Make().lea(R6, ((ExprRegister.R6 + ExprRegister.R4) + 0x7F4EC5E6));


            node = vcontext.Codes.Last;
            exp = vcontext.TraceStrict(node, ref node.O1);




            Assert.IsTrue(exp.Calculable);


        }


        //[TestMethod()]
        //public void xchgTest()
        //{
        //    Context vcontext = new Context(true);


        //    vcontext.Make().mov(R0, 1);
        //    vcontext.Make().xchg(R0, R1);
        //    vcontext.Make().mov(R3, R1);

        //    var node = vcontext.Codes.Last;
        //    var inst = vcontext.TraceUp(node, node.O1);

        //    var exp = vcontext.TraceStrict(node, ref node.O1);


        //}


    }
}